// Copyright (c) 2023 刻BITTER
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.


#include <Arduino.h>

#include "oled_basic_v1.h"

// 具体硬件的底层驱动，只选择一个要用的include 进来，其他驱动的依赖就不会被引入。比如只用SPI 通信，就不需要Wire 库。
#include "oled_basic_1316_arduino_spi.h"
// #include "oled_basic_1306_arduino_wire.h"


// 定义SPI 操作引脚，SCK 和MOSI引脚由硬件SPI 自己管理
constexpr auto OLED_CS = 7;
constexpr auto OLED_DC = 8;

// 选择具体屏幕的底层驱动，同时传入SPI 引脚。ArduinoSpi1316_12832 对应12832 的屏幕，
// 如果有多个驱动方式相同的不同尺寸屏幕，都会放在一个头文件里
using LowLevel = oled_basic::ArduinoSpi1316_12832<OLED_CS, OLED_DC>;

// 实例化顶层驱动
using Oled = oled_basic::OledDriver<LowLevel>;

// 如果用I2C 总线，就不需要传入其他的引脚作为参数，全部由Wire 库管理
// using Oled = oled_basic::OledDriver<oled_basic::ArduinoWire1306_12864>;


// 选择待会用来显示文本的字体，用一个常量表示，方便修改。
// ascii_8x16_no_lower_case 是ascii_8x16 字库裁剪掉小写字母部分。
// 字体的命名规则都是三段式，ascii 表示名称，8x16 表示单个符号的宽度和高度，其中高度必须是8 的倍数。
// 如果字体还有一些别的特征，而且没有体现在字体名称中，则用后缀标记。no_lower_case 就是指没有小写字母。
constexpr auto &TEXT_FONT = oled_basic::ascii_8x16_no_lower_case;

void setup() {
    pinMode(OLED_CS, OUTPUT);
    pinMode(OLED_DC, OUTPUT);

    // 底层驱动使用SPI 库时不管初始化部分，需要手动写
    // 通信时的参数是自动设置的
    SPI.begin();

    // Wire.begin();

    // 执行屏幕对应的初始化代码
    Oled::run_init_sequence();
    
    // 显示两个文本，0，1 表示x，y 坐标，或者说是列和页地址。
    // 对于12832 的屏幕，列地址从0 到127，可以单像素点定位；
    // 页地址从0 到3，每个页代表屏幕上8 个像素高度的横条，其中的每列8 个像素就对应显存中的一个字节。
    // 向屏幕传送显示数据时，只能对应显存里的字节一个一个传，所以一次传送至少覆盖某个页中的一列8 个像素，也可以说是一行中的8 个像素。
    // 如果想实现高度上的单像素点定位，需要做不少额外的处理，可能需要个显示缓冲区，跟这个精简OLED 库的目标不符。
    Oled::put_str("NUM!", TEXT_FONT, 0, 1);
    Oled::put_str("NUM!", TEXT_FONT, 20, 0);
    
    int8_t num = -100;

    while (1) {
        // 用12x24 的大号数字字体显示数字，24 / 8 = 3，也就是要占用3 页的高度。
        // 数字字体里包含十个数字，加上小数点、负号和斜杠，一共13 个符号。字体本身占用的空间是3 * 12 * 13 = 468 字节。
        // 最后一个参数oled_basic::write_mode::inverse 是可选参数，表示反色显示，默认不反色。
        // put_str 最后也可以加上反色参数。
        // 返回值表示显示内容后的下一列地址。如果在第0 列显示了一个12x24 的数字，返回值就是0 + 12 = 12。
        uint8_t col = Oled::put_signed(num++, oled_basic::big_digit_12x24, 59, 1, oled_basic::write_mode::inverse);
        
        // num 范围是-128 到127，所以最多显示三位数加一个符号，文字占用12 * 4 的宽度。
        // 显示了较大数字后，再显示小数字，必须清空后面空出来的位置
        // 比如先显示-100，然后变成-99，如果不做清空，实际显示出来就是-990。
        // 整体清屏再重写会产生一个空白帧，导致闪烁，所以只清空数字后面多出来的部分
        if(col < 59 + 12 * 4) {
            Oled::fill_area(0, col, 1, 59 + 12 * 4 - col, 3);
        }

        // fill_area 的功能是用指定的一个字节数据重复填充屏幕上一块矩形区域，
        // 如果指定的数据是0，效果就是清空区域
        
        delay(500);
    }
}

void loop() {}